package com.tanhua.server.service;

import com.alibaba.fastjson.JSON;
import com.tanhua.commons.template.AipFaceTemplate;
import com.tanhua.commons.template.HuanXinTemplate;
import com.tanhua.commons.template.OssTemplate;
import com.tanhua.commons.template.SmsTemplate;
import com.tanhua.domain.db.User;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.Friend;
import com.tanhua.domain.mongo.UserLike;
import com.tanhua.domain.vo.ErrorResult;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.domain.vo.UserInfoVo;
import com.tanhua.domain.vo.UserLikeVo;
import com.tanhua.dubbo.api.UserApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.mongo.FriendApi;
import com.tanhua.dubbo.api.mongo.RecommendUserApi;
import com.tanhua.dubbo.api.mongo.UserLikeApi;
import com.tanhua.server.interceptor.UserHolder;
import com.tanhua.server.utils.JwtUtils;
import org.apache.catalina.security.SecurityUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.zookeeper.util.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service  // 创建对象，加入容器，加入容器的对象名称默认是类名称，首字母小写
public class UserService {

    // 注入dubbo服务接口的代理对象
    // 导入的包：org.apache.dubbo.config.annotation.Reference
    @Reference
    private UserApi userApi;
    @Reference
    private UserInfoApi userInfoApi;
    @Reference
    private RecommendUserApi recommendUserApi;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    private OssTemplate ossTemplate;
    @Autowired
    private AipFaceTemplate aipFaceTemplate;
    @Autowired
    private SmsTemplate smsTemplate;
    @Autowired
    private HuanXinTemplate huanXinTemplate;


    // 存储验证码到redis中的前缀
    private final String SMS_KEY = "SMS_KEY_";
    private final String TOKEN_KEY = "TOKEN_KEY_";
    @Value("${tanhua.secret}")
    private String secret;

    /**
     * 测试方法1：查询
     */
    public ResponseEntity<Object> findByMobile(String mobile) {
        User user = userApi.findByMobile(mobile);
        return ResponseEntity.ok(user);
    }

    /**
     * 测试方法2：保存
     */
    public ResponseEntity<Object> save(User user) {
        try {
            int i = 1/0;
            // 正常执行
            Long userId = userApi.save(user);
            return ResponseEntity.ok(userId);
        } catch (Exception e) {
            // 异常
            Map<String,Object> errorMap = new HashMap<>();
            errorMap.put("errCode","99999");
            errorMap.put("errMessage","添加用户出现错误！");

            e.printStackTrace();
            return ResponseEntity.status(500).body(errorMap);
        }
    }

    /**
     * 接口名称：登录第一步---手机号登录
     * 需求描述：发送验证码
     */
    public ResponseEntity<Object> login(String phone) {
        //1. 生成验证码
        String code = (int)((Math.random()*9 + 1) * 100000) + "";
        //2. 调用template实现发送验证码
        //smsTemplate.sendSms(phone,code);
        code = "123456";
        Long userId = userApi.findByMobile(phone).getId();


        //获取redis冻结用户
        String key="freezingTime_"+userId+"_登录冻结";
        String value=redisTemplate.opsForValue().get(key);
        //判断redis中冻结用户是否存在，存在不能登入
        if(redisTemplate.hasKey(key)){
            return ResponseEntity.status(500).body(value);
        }


        //3. 在redis中保存验证码
        redisTemplate.opsForValue().set(SMS_KEY+phone,code, Duration.ofMinutes(5));
        return ResponseEntity.ok(null);
    }

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 接口名称：登录第二步---验证码校验
     * 接口路径：POST/user/loginVerification
     */
    public ResponseEntity<Object> loginVerification(String phone, String verificationCode) {



        // 保存验证码在redis中的key
        String key = SMS_KEY + phone;
        //1. 从redis中获取验证码
        String redisCode = redisTemplate.opsForValue().get(key);

        //2. 验证码校验： 用户输入的验证码 与 redis中的验证码校验
        if (StringUtils.isEmpty(redisCode) || !redisCode.equals(verificationCode)) {
            //2.1 验证码校验失败, 返回错误
            return ResponseEntity.status(500).body(ErrorResult.loginError());
        }

        //3. 验证码校验成功，删除redis中的验证码
        redisTemplate.delete(key);

        // 日志类型，默认0101登陆
        String type = "0101";
        // 默认不是新用户
        boolean isNew = false;
        //4. 根据手机号查询用户是否存在, 如果用户不存在，就自动注册
        User user = userApi.findByMobile(phone);
        if (user == null) {
            // 根据手机号码没有查询到用户，说明是一个新用户，就完成自动注册
            user = new User();
            user.setMobile(phone);
            user.setPassword(DigestUtils.md5Hex("123456"));
            // 保存用户, 获取新增用户的主键id
            Long userId = userApi.save(user);
            // 设置到user对象中（无论新老用户都可以获取到用户id）
            user.setId(userId);

            // 新用户
            isNew = true;

            // 新用户注册到环信 (mysql中的用户id与环信的用户id一致)
            huanXinTemplate.register(user.getId());

            // 记录日志类型是0102注册
            type = "0102";
        }

        // 【发送mq日志消息，交给消费者工程记录日志】
        // 通过map封装消息内容
        Map<String,String> map = new HashMap<>();
        map.put("userId",user.getId().toString());
        map.put("type",type);
        map.put("date",new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        try {
            // 发送消息
            rocketMQTemplate.convertAndSend("tanhua-log-152",JSON.toJSONString(map));
        } catch (MessagingException e) {
            e.printStackTrace();
        }

        // 通过jwt生成token
        // A. 生成token
        String token = JwtUtils.createToken(user.getId(), phone, secret);
        // B. 对象转换为json
        String userJsonString = JSON.toJSONString(user);
        // C. 把生成的token存储到redis中，存储的key：TOKEN_KEY_ + token, 存储的value：用户的json字符串
        redisTemplate.opsForValue().set(TOKEN_KEY+token,userJsonString,Duration.ofHours(4));

        //5. 返回
        Map<String,Object> resultMap = new HashMap<>();
        boolean verification=true;
        resultMap.put("token",token);
        resultMap.put("isNew",isNew);
        resultMap.put("verification",verification);
        return ResponseEntity.ok(resultMap);
    }

    /**
     * 接口名称：新用户---1填写资料
     * 需求描述：保存用户详情（tb_user_info）
     */
    public ResponseEntity<Object> saveUserInfo(UserInfo userInfo, String token) {
        //1. 根据token，获取用户信息 （拿到用户id）
        User user = this.findUserByToken(token);
        if (user == null) {
            return ResponseEntity.status(500).body(ErrorResult.error());
        }

        //2. 调用Api，保存用户详情
        userInfo.setId(user.getId());
        userInfoApi.save(userInfo);
        return ResponseEntity.ok(null);
    }

    /**
     * 根据token获取redis中存储的用户信息
     * @param token
     * @return
     */
    public User findUserByToken(String token) {
        //1. 构造redis中token的key
        String key = TOKEN_KEY + token;
        //2. 获取redis中存储的token对用的用户json字符串
        String userJsonString = redisTemplate.opsForValue().get(key);
        if (userJsonString == null) {
            return null;
        }
        //3. 转换为对象
        User user = JSON.parseObject(userJsonString, User.class);
        //4. 刷新token的有效时间
        redisTemplate.opsForValue().set(key,userJsonString,Duration.ofHours(4));
        //redisTemplate.expire(key,4, TimeUnit.HOURS);
        return user;
    }

    /**
     * 接口名称：新用户---2选取头像
     * 需求描述：设置用户头像，修改tb_user_info用户详情表
     */
    public ResponseEntity<Object> updateHead(
            MultipartFile headPhoto, String token) throws IOException {
        //0. 根据token获取登陆用户信息
        User user = this.findUserByToken(token);

        //1. 根据上传的文件进行人脸检测
        boolean detect = aipFaceTemplate.detect(headPhoto.getBytes());
        if (!detect) {
            // 返回错误
            return ResponseEntity.status(500).body(ErrorResult.faceError());
        }

        //2. 人脸检测通过，需要把图片上传到阿里云oss存储
        //2.1 获取文件名
        String fileName = headPhoto.getOriginalFilename();
        //2.2 图片上传
        String url = ossTemplate.upload(fileName, headPhoto.getInputStream());

        //3. 修改用户详情表，设置用户头像地址
        UserInfo userInfo = new UserInfo();
        // 修改条件：主键id
        userInfo.setId(user.getId());
        // 修改内容： 头像地址
        userInfo.setAvatar(url);
        userInfoApi.update(userInfo);
        return ResponseEntity.ok(null);
    }


    /**
     * 接口名称：修改手机号 - 3 保存
     * @param phone
     * @return
     */
    public ResponseEntity<Object> savePhone(String phone) {
        User user = UserHolder.get();
        user.setMobile(phone);
        userApi.updatePhone(user);
        return ResponseEntity.ok(null);
    }

    /**
     * 接口名称：用户资料 - 读取
     * 需求分析：根据用户id查询用户详情
     * @param userID
     * @param huanxinID
     */
    public ResponseEntity<Object> findUserInfoById(Long userID, Long huanxinID) {
        //1. 根据token获取用户
        Long userId = UserHolder.getUserId();

        //2. 如果userID不为NULL，根据用户id查询
        if (userID != null) {
            userId = userID;
        }else if(huanxinID != null){
            userId = huanxinID;
        }


        //2.根据用户id查询用户详情
        UserInfo userInfo = userInfoApi.findById(userId);

        //3.定义一个vo：UserInfoVo,用于封装返回的json数据
        UserInfoVo vo = new UserInfoVo();
        //对象拷贝: 参数1：数据源； 参数2：目标对象
        BeanUtils.copyProperties(userInfo,vo);
        // 处理年龄
        if (userInfo.getAge() != null) {
            vo.setAge(userInfo.getAge().toString());
        }
        return ResponseEntity.ok(vo);
    }

    /**
     * 接口名称：用户资料 - 保存
     * 需求分析：修改用户详情  tb_user_info
     */
    public ResponseEntity<Object> updateUserInfo(UserInfo userInfo) {
        // 获取用户信息
        User user = UserHolder.get();
        // 设置用户id
        userInfo.setId(user.getId());
        // 保存用户详情
        userInfoApi.update(userInfo);
        return ResponseEntity.ok(null);
    }

    @Reference
    private UserLikeApi userLikeApi;

    /**
     * 接口名称：互相喜欢，喜欢，粉丝
     */
    public ResponseEntity<Object> queryCounts() {
        //1. 获取登陆用户id
        Long userId = UserHolder.getUserId();
        //2. 根据登陆用户id查询：喜欢、粉丝、互相喜欢
        Long loveCount = userLikeApi.queryLoveCount(userId);
        Long fanCount = userLikeApi.queryFanCount(userId);
        Long eachLoveCount = userLikeApi.queryEachLoveCount(userId);
        //3. 定义map，封装返回结果
        Map<String,Integer> resultMap = new HashMap<>();
        resultMap.put("loveCount",loveCount.intValue());
        resultMap.put("fanCount",fanCount.intValue());
        resultMap.put("eachLoveCount",eachLoveCount.intValue());
        return ResponseEntity.ok(resultMap);
    }

    /**
     * 接口名称：互相喜欢、喜欢、粉丝、谁看过我  (列表)
     * @param type
     *    1 互相关注： 查询好友表 tanhua_users
     *    2 我关注：  查询喜欢表 user_like
     *    3 粉丝:    查询喜欢我 user_like
     *    4 谁看过我：查询访客表 visitors
     */
    public ResponseEntity<Object> queryUserLikeList(
            Integer type, Integer page, Integer pagesize) {
        //1. 获取登陆用户id
        Long userId = UserHolder.getUserId();
        //2. 分页查询结果
        PageResult pageResult = null;
        //3. 根据前端传入的type，判断查询类型
        switch (type) {
            case 1: //互相关注
                pageResult =userLikeApi.queryEachLoveList(userId,page,pagesize);
                break;
            case 2://我关注
                pageResult =userLikeApi.queryLoveList(userId,page,pagesize);
                break;
            case 3://粉丝
                pageResult =userLikeApi.fanList(userId,page,pagesize);
                break;
            case 4://访客
                pageResult =userLikeApi.visitorList(userId,page,pagesize);
                break;
        }
        //4. 获取查询数据
        List<Map<String,Object>> list  = (List<Map<String, Object>>) pageResult.getItems();
        //5. 创建并封装vo集合
        List<UserLikeVo> voList = new ArrayList<>();
        for (Map<String, Object> map : list) {
            // 获取用户id、缘分值
            Long uid = (Long) map.get("uid");
            Long score = (Long) map.get("score");
            // 创建vo对象
            UserLikeVo vo = new UserLikeVo();
            // 封装vo对象：查询返回的用户信息
            UserInfo userInfo = userInfoApi.findById(uid);
            if (userInfo != null) {
                // 封装vo对象：对象拷贝
                BeanUtils.copyProperties(userInfo, vo);
            }
            // 封装vo对象：设置缘分值
            vo.setMatchRate(score.intValue());
            // 添加到集合
            voList.add(vo);
        }
        //6. 把vo设置到分页对象中
        pageResult.setItems(voList);
        return ResponseEntity.ok(pageResult);
    }

    @Reference
    private FriendApi friendApi;
    /**
     * 接口名称：粉丝 - 喜欢
     */
    public ResponseEntity<Object> fanLike(Long likeUserId) {
        //0. 获取登陆用户id
        Long userId = UserHolder.getUserId();
        //1. 从粉丝列表中移除数据
        userLikeApi.remove(likeUserId,userId);
        //2. 注册好友关系
        friendApi.addContacts(userId,likeUserId);
        //3. 保存好友关系到环信
        huanXinTemplate.contactUsers(userId,likeUserId);
        return ResponseEntity.ok(null);
    }

    /**
     * 接口名称：取消喜欢
     */
    public ResponseEntity<Object> unlike(Long unLikeUserId) {
        //0. 获取登陆用户id
        Long userId = UserHolder.getUserId();
        //3.在粉丝列表中添加一条他喜欢我的数据
        UserLike userLike = new UserLike();
        userLike.setUserId(unLikeUserId);
        userLike.setLikeUserId(userId);
        userLike.setCreated(System.currentTimeMillis());
        userLikeApi.save(userLike);
        //1.从好友列表里面删除
        friendApi.removeContacts(userId,unLikeUserId);
        //2.从环信中删除好友关系（待续）
        huanXinTemplate.delContact(userId,unLikeUserId);
        return ResponseEntity.ok(null);
    }


    public Boolean alreadyLove(Long uid) {
        Long loginId = UserHolder.getUserId();
        Integer relationship = recommendUserApi.getRelationship(loginId, uid);
        if (relationship == 1 || relationship == 2) {
            return true;
        }
        return false;
    }
}
